package com.hllxd.hism.star.basic.service.service;

import com.hllxd.hism.star.basic.service.dao.influx.DapanInfluxMapper;
import com.hllxd.hism.star.basic.service.dao.mapper.doris.DimensionFlowBiMapper;
import com.hllxd.hism.star.basic.service.dao.mapper.doris.DorisDapanMapper;
import com.hllxd.hism.star.basic.service.dao.mapper.mis.DapanDimConfigMapper;
import com.hllxd.hism.star.basic.service.dao.mapper.mis.DapanMapper;
import com.hllxd.hism.star.basic.service.dao.mapper.mis.MetricDefineMapper;
import com.hllxd.hism.star.basic.service.model.DimTableDataEntity;
import com.hllxd.hism.star.basic.service.model.RealtimeMetricDefineEntity;
import com.hllxd.hism.star.basic.service.model.dapan.*;
import com.hllxd.hism.star.basic.service.model.metric.MetricDefineEntity;
import com.hllxd.hism.star.basic.service.util.CommonUtils;
import com.hllxd.hism.star.basic.service.util.TimeUtil;
import com.github.pagehelper.StringUtil;
import com.hllxd.hism.star.basic.service.model.DimIndexChartEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 计算任务白名单管理
 *
 * @author xiyong.lxy
 * @date 2020-10-12 16:51
 */
@Slf4j
@Service("dapanService")
public class DapanServiceImpl implements DapanService {

    @Autowired
    private DapanMapper dapanMapper;

    @Autowired
    private DorisDapanMapper dorisDapanMapper;

    @Autowired
    private MetricDefineMapper metricDefineMapper;


    @Autowired
    private DapanInfluxMapper dapanInfluxMapper;

    @Autowired
    private DapanDimConfigMapper dapanDimConfigMapper;

    @Autowired
    private DimensionFlowBiMapper dimensionFlowBiMapper;

    private final DateFormat YYYY_MM_DD = new SimpleDateFormat("yyyyMMdd");


    @Override
    public List<MetricDefineEntity> listDapanSeriesAll(String dt, String dt2, String biz) {

        // 指定对应的指标（每个人指定不同的页面关注的指标可能不同，可以单独获取）
        log.info("listDapanSeriesAll: {}, {}, {}.",dt, dt2, biz);
        return dorisDapanMapper.listDapanSeriesAll(dt, dt2, biz);
    }

//    @Override
//    public List<MetricDefineEntity> getYeWuResult2(String dt, String dt2, String biz, String userid) {
//
//        // 指定对应的指标（每个人指定不同的页面关注的指标可能不同，可以单独获取）
//        log.info("getYeWuResult2: {}, {}, {}, {}.",dt, dt2, biz, userid);
//        List<MetricDefineEntity> result;
//        if (userid == null) {
//            result = dapanMapper.listDapanSeriesAll(dt, dt2, biz);
//        } else {
//            result = dapanMapper.listDapanSeries(dt, dt2, biz, userid);
//        }
//        for (MetricDefineEntity entity : result) {
//            entity.compareEntity = DapanPkg.transToCompare(entity.series);
//        }
//        return result;
//    }

    public static Map<String,MetricDefineEntity> convertListToMap2(List<MetricDefineEntity> dataList) {
        Map<String,MetricDefineEntity> resultMap = new HashMap<>();
        for (MetricDefineEntity data : dataList) {
            String key = data.metaId;
            resultMap.put(key, data);
        }
        return resultMap;
    }


    @Override
    public List<MetricDefineEntity> getYeWuResult3(String dt, String dt2, String biz, String metaid, String userid) {

        // 读取metaId定义
        List<MetricDefineEntity> result = metricDefineMapper.list4DapanSeries3(dt, dt2, biz, metaid);

        // 读取metaId对应的数据
        List<MetricDefineEntity> dataList = dorisDapanMapper.listDapanSeries3(dt, dt2, biz, metaid);
        Map<String, MetricDefineEntity> dataMap = convertListToMap2(dataList);
        for(MetricDefineEntity item: result) {
            MetricDefineEntity data = dataMap.get(item.metaId);
            if(data==null) {
                continue;
            }
            item.series = sortSeries(data.series);
        }

        // 读取大盘当前值
        List<DapanCountEntity> items = dorisDapanMapper.listDapan3(dt2, biz, metaid);
        for (MetricDefineEntity entity : result) {
            for (DapanCountEntity item : items) {
                log.info("{}, {}", item.id, entity.metaId);
                if (item.id.equals(entity.metaId)) {
                    entity.compareEntity = item;
                    break;
                }
            }
        }
        return result;
    }

    public static String sortSeries(String str) {
        if(str==null || str.length()<3) {
            return null;
        }
        String[] strings = str.split(",");
        for (int i = 0; i < strings.length; i++) {
            strings[i] = strings[i].trim();
        }
        Arrays.sort(strings);
        return String.join(",", strings);
    }


//    @Override
//    public ZhibiaoDashboard listZhibiaoDashboard(String dt) {
//
//        // 指定对应的指标（每个人指定不同的页面关注的指标可能不同，可以单独获取）
//        // content: 当前值，前1天的值， 周均值，上周均值对比
//        ZhibiaoDashboard result = new ZhibiaoDashboard();
//        result.addMetric("20220611", "all", "tag", "cost_jl", "22.2,-0.2021");
//        result.addMetric("20220611", "all", "tag", "cpm1", "22.2,-0.2021");
//        result.addMetric("20220611", "all", "tag", "cpm2", "2222.2,-0.2021");
//        result.addMetric("20220611", "all", "tag", "cost", "233.2,-0.2021");
//        result.addMetric("20220611", "all", "tag", "cash", "222.2,0.2021");
//        return result;
//    }


//    @Override
//    public DapanPkg getYeWuResult(String dt, String dt2, String biz) {
//        String dayIDAfter = null;
//        try {
//            synchronized (YYYY_MM_DD) {
//                dayIDAfter = TimeUtil.getDayAfter(YYYY_MM_DD.parse(dt2), 1, "yyyyMMdd");
//            }
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }
//        log.info("getExpList {} {} {}", dt, dayIDAfter, biz);
//
//        // 读取大盘数据
//        List<QuotaEntity> quotaList = dapanMapper.listDapan(dt, dayIDAfter, biz, null);
//        List<DapanCountEntity> dapanCountEntityList = new ArrayList<>();
//        List<DapanYeWuEntity> dapanYeWuEntityList = DapanPkg.buildYewuList(quotaList);
//        log.info("data: {}", JSON.toJSONString(dapanYeWuEntityList));
//        if (dapanYeWuEntityList != null) {
//            dapanCountEntityList = DapanPkg.transToCompare(dapanYeWuEntityList);
//        } else {
//            log.warn("dapanCountEntityList is null");
//        }
//        return DapanPkg.buildPkg(null, dapanYeWuEntityList, dapanCountEntityList, null);
//    }

    @Override
    public QuotaResult getQuotaList(String dt, String dt2, String biz, String id) {
        if (CommonUtils.isNull(dt, dt2, biz, id)) {
            log.warn("getQuotaList has null args.");
            return null;
        }
        log.info("getQuotaList: dt {},dt2 {},id {},pageName:{}", dt, dt2, id, biz);
        if (null == biz) {
            biz = "zp";
        }
        dt = dt.replace("-", "");
        dt2 = dt2.replace("-", "");

        List<QuotaEntity> quotaEntityList = dorisDapanMapper.listDapanById(dt, dt2, id, biz);
        log.info("quotaEntityList: {}", quotaEntityList.size());
        QuotaResult quotaResult = QuotaResult.build(quotaEntityList);
        if (quotaResult.getQuotaEntityList() != null) {
            log.info("quotaResult size:{}", quotaResult.getQuotaEntityList().size());
        }
        return buildSeriesData(quotaResult);
    }

    @Override
    public List<List<Object>> metricSeries(String dt, String dt2, String metric, String pageName) {
        if (StringUtil.isEmpty(pageName)) {
            pageName = "zp";
        }
        List<QuotaEntity> items = dorisDapanMapper.metricSeries("2021-01-01 00:00:00", dt, metric, pageName);
        log.info("size: {}", items.size());
        List<List<Object>> res = new ArrayList<>();
        for (QuotaEntity item : items) {
            List<Object> row = new ArrayList<>();
            row.add(item.reportDate);
            row.add(item.content);
            res.add(row);
        }
        return res;
    }

    @Override
    public SeriesResult listSeries(String dt, String dt2, String tag, String table, String dimcombo) {
        log.info("listSeries:{},{}, influx: {} {}", dt, dt2, table, tag);

        if (CommonUtils.isNull(dt, dt2, tag, table)) {
            log.warn("listSeries has null args");
            return null;
        }
        Long ts = TimeUtil.influxToTs(dt + ":00");
        Long ts2 = TimeUtil.influxToTs(dt2 + ":00");
        if (ts == null || ts2 == null) {
            log.warn("dt, dt2 is invalid. {},{}.", dt, dt2);
            return null;
        }
        dt = org.influxdb.impl.TimeUtil.toInfluxDBTimeFormat(ts);
        dt2 = org.influxdb.impl.TimeUtil.toInfluxDBTimeFormat(ts2);

        // 根据pageName [58,ganji,chinahr]进行适配对应的  database， tableName, tag, dtType='1m'
        SeriesResult seriesResult = listSeries2(dt, dt2, tag, table, dimcombo);
        if (seriesResult == null) {
            log.warn("listSeries seriesResult result is null");
            return null;
        }
        if (seriesResult.getPoints() != null) {
            log.info("listSeries seriesResult result size:{}", seriesResult.getPoints().size());
        }
        return seriesResult;
    }

    public static Map<String,QuotaEntity> convertListToMap(List<QuotaEntity> dataList) {
        Map<String,QuotaEntity> resultMap = new HashMap<>();
        for (QuotaEntity data : dataList) {
            String key = data.metaID;
            resultMap.put(key, data);
        }
        return resultMap;
    }
    @Override
    public List<DapanChartEntity> listAllMetricSeries(String startDt, String endDt, String biz, String appDiv, String platform) {
        if (StringUtil.isEmpty(biz)) {
            biz = "zp";
        }
        if (StringUtil.isEmpty(platform)) {
            platform = "ALL";
        }
        // todo：读取数据源，应该是读取表数据app_zp_dimension_flow_bi_v3
        List<QuotaEntity> items = dorisDapanMapper.listAllMetricSeries(startDt, endDt, biz, appDiv, platform);
        log.info("list data size: {}.", items.size());
        List<QuotaEntity> itemsMeta = metricDefineMapper.listMetric4Series(appDiv);
        Map<String, QuotaEntity> mapMeta = convertListToMap(itemsMeta);
        for(QuotaEntity item: items) {
            QuotaEntity meta = mapMeta.get(item.metaID);
            if(meta==null) {
                continue;
            }
            item.indexTitle = meta.indexTitle;
        }

        //根据meta id聚合
        Map<String, List<Pair<String, List<Object>>>> groupMap = items.stream().map(x -> {
            List<Object> row = new ArrayList<>();
            row.add(x.getReportDate());
            row.add(x.getContent());
            return Pair.of(x.getMetaID(), row);
        }).collect(Collectors.groupingBy(Pair::getLeft));

        Map<String, QuotaEntity> metaIdToEntity = items.stream().collect(
                Collectors.toMap(QuotaEntity::getMetaID, v -> v, (v1, v2) -> v1)
        );

        ArrayList<DapanChartEntity> entities = new ArrayList<>();
        //转换为结果集
        groupMap.forEach((k, v) -> {
            List<List<Object>> lists = new ArrayList<>();
            for (Pair<String, List<Object>> pair : v) {
                lists.add(pair.getRight());
            }
            DapanChartEntity chartEntity = new DapanChartEntity();
            chartEntity.setMetaId(k);
            chartEntity.setIndexTitle(metaIdToEntity.get(k).getIndexTitle());
            chartEntity.setChartData(lists);
            entities.add(chartEntity);
        });

        return entities;
    }


    /**
     * 无线查询3年的横屏记录
     *
     * @param quotaResult 结果解析
     * @return 结构体
     */
    private QuotaResult buildSeriesData(QuotaResult quotaResult) {
        if (quotaResult == null || quotaResult.getQuotaEntityList() == null) {
            log.warn("buildSeriesData quotaResult is null");
            return null;
        }
        List<List<Object>> countPoints = new ArrayList<>();
        List<List<Object>> amountPoints = new ArrayList<>();
        for (QuotaEntity entity : quotaResult.getQuotaEntityList()) {
            String content = entity.getContent();
            BigDecimal val = Utils.toBigDecimal(content);
            String date = entity.reportDate.substring(0, 10);
            if (val != null) {
                List<Object> countPoint = new ArrayList<Object>() {{
                    add(date);
                    add(val);
                }};
                countPoints.add(countPoint);
            } else {
                countPoints.add(new ArrayList<Object>() {{
                    add(date);
                    add(0);
                }});
            }
        }
        return QuotaResult.buildPoints(quotaResult, countPoints, amountPoints);
    }

    private SeriesResult listSeries2(String dt, String dt2, String tag, String table, String dimcombo) {
        SeriesResult seriesResult;
        if (dimcombo != null) {
            seriesResult = dapanInfluxMapper.listRtSeries(dt, dt2, tag, table, dimcombo);
        } else {
            seriesResult = dapanInfluxMapper.listBcpSeries(dt, dt2, tag, table);
        }

        if (seriesResult == null) {
            log.warn("result is null. {},{},{}", table, dt, dt2);
            return null;
        }
        if (seriesResult.points != null) {
            log.info("result size:{}", seriesResult.points.size());
        }
        List<List<Object>> points = seriesResult.points;
        if (points != null && points.size() > 1) {
            int lastIdx = points.size();
            double val = SeriesResult.fmtNull(points.get(lastIdx - 1).get(1));
            double val2 = SeriesResult.fmtNull(points.get(lastIdx - 2).get(1));
            if (val < 1 || val2 > val * 10) {
                log.info("remove last -1 points.size: {}", lastIdx);
                points.remove(lastIdx - 1);
            }
            if (val2 < 1) {
                log.info("remove last -2 points.size: {}", lastIdx);
                points.remove(lastIdx - 2);
            }
        }
        return seriesResult;
    }

//    private BigDecimal loadCount(String content, String pageName) {
//        if (content == null || content.isEmpty()) {
//            return null;
//        }
//        String[] items = content.split(",");
//        if (items.length == 2 && "shop".equals(pageName)) {
//            return new BigDecimal(items[1]);
//        }
//        return new BigDecimal(items[0]);
//    }

//    public ReportSummary summaryV2(String dt, String dt2, String pageName) {
//        log.info("dt:{},dt2:{},pageName:{}", dt, dt2, pageName);
//        List<BillReportEntity> billReportEntities = dapanMapper.listSummary(dt, dt2, pageName);
//        ReportSummary reportSummary = new ReportSummary();
//        reportSummary.setBillReportEntities(billReportEntities);
//        return reportSummary;
//    }

//    private List<BillReportEntity> getMaxDtList(List<BillReportEntity> billReportEntityList, BillReportEntity maxEntity) {
//        if (billReportEntityList == null || billReportEntityList.size() == 0 || maxEntity == null) {
//            log.warn("getLastEntity billReportEntityList or maxEntity is null");
//            return null;
//        }
//        List<BillReportEntity> result = new ArrayList<>();
//        for (BillReportEntity entity : billReportEntityList) {
//            try {
//                Date itemDt = TimeUtil.dateParse(entity.getReportdate(), "yyyyMMdd");
//                Date maxDt = TimeUtil.dateParse(maxEntity.getReportdate(), "yyyyMMdd");
//                if (itemDt.compareTo(maxDt) == 0) {
//                    result.add(entity);
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//        log.info("getMaxDtList result size:{}", result.size());
//        return result;
//    }

//    private BillReportEntity getMaxDtEntity(List<BillReportEntity> billReportEntityList) {
//        if (billReportEntityList == null || billReportEntityList.size() == 0) {
//            log.warn("getLastEntity billReportEntityList is null");
//            return null;
//        }
//        BillReportEntity entity = new BillReportEntity();
//        entity.setReportdate("19700101");
//        for (BillReportEntity item : billReportEntityList) {
//            try {
//                Date itemDt = TimeUtil.dateParse(item.getReportdate(), "yyyyMMdd");
//                Date initDt = TimeUtil.dateParse(entity.getReportdate(), "yyyyMMdd");
//                if (itemDt.after(initDt)) {
//                    entity = item;
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//        log.info("getMaxDtEntity entity:{}", entity);
//        return entity;
//    }


    @Override
    public List<QuotaEntity> getWxExpList(String dt, String dt2, String biz, String appdiv, String zgroup) {
        if (StringUtil.isEmpty(biz)) {
            biz = "zp";
        }
        log.info("dt: {}, dt2:{}, biz:{}, appdiv:{}, zgroup:{}", dt, dt2, biz, appdiv, zgroup);
        try {
            dt = TimeUtil.getDayAfter(YYYY_MM_DD.parse(dt2), 0, "yyyy-MM-dd 00:00:00");
            log.info("dt: {},{},biz:{}.", dt, dt2, biz);
            Integer zlevel = (zgroup == null) ? 1 : null;

            List<QuotaEntity>  items = metricDefineMapper.listMetric4DapanByDiv(dt, dt, biz, appdiv, zgroup, zlevel, "ALL");
            List<QuotaEntity>  dataList = dorisDapanMapper.listDapanByDiv(dt, dt, biz, appdiv, zgroup, zlevel, "ALL");
            Map<String, QuotaEntity> mapData = convertListToMap(dataList);
            boolean hasData = false;
            for(QuotaEntity item: items) {
                QuotaEntity data = mapData.get(item.metaID);
                if(data==null) {
                    log.info("metaId is no data. {}",item.metaID);
                    continue;
                }
                hasData = true;
                item.content = data.content;
                item.contentInc1 = data.contentInc1;
                item.contentInc7 = data.contentInc7;
                item.reportDate = data.reportDate;
                item.createTime = data.createTime;
            }
            if(! hasData) {
                log.info("{},{} no data ...",dt,dt2);
                return null;
            }
            return items;
        } catch (Exception e) {
            log.error("getWxExpList error", e);
            return null;
        }
    }

//    @Override
//    public List<QuotaEntity> listMetricAll(String dt, String dt2, String biz, String appdiv) {
//        if (StringUtil.isEmpty(biz)) {
//            biz = "zp";
//        }
//        try {
//            log.info("dt2: {}.", dt2);
//            //时间转化TimeUtil.getDayAfter(YYYY_MM_DD.parse(dt2), 0, "yyyy-MM-dd 00:00:00");
//            dt = dt2 + " 00:00:00";
//            log.info("mapper params: {},{},{},{}.", dt, dt, biz, appdiv);
//            List<QuotaEntity> quotaEntities = dapanMapper.listDapanByDiv(dt, dt, biz, appdiv, null, null, "ALL");
//            for (QuotaEntity entity : quotaEntities) {
//                String dataUnitType = entity.getDataUnitType();
//                String content = entity.getContent();
//                String newContent = formatData(content, dataUnitType);
//                entity.setContent(newContent);
//            }
//            return quotaEntities;
//        } catch (Exception e) {
//            log.error("getWxExpList error", e);
//            return null;
//        }
//    }

    public String formatData(String content, String dataUnitType) {

        if (content == null || "".equals(content)) {
            return content;
        }
        try {
            switch (dataUnitType) {
                case "amount":
                    content = new BigDecimal(content).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString();
                    break;
                case "percent":
                    content = new BigDecimal(content).multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP) + "%";
                    break;
                case "avg":
                    DecimalFormat df1 = new DecimalFormat("###,###.00");
                    BigDecimal bigDecimal = new BigDecimal(content).setScale(2, RoundingMode.HALF_UP);
                    content = df1.format(bigDecimal);
                    break;
                case "count":
                default:
                    DecimalFormat df2 = new DecimalFormat("###,###");
                    content = df2.format(new BigDecimal(content));
            }
        } catch (Exception e) {
            log.error(content + ":" + dataUnitType, e);
            return content;
        }

        return content;
    }

    @Override
    public List<RealtimeMetricDefineEntity> listRtKanban(String dt, String dt2, String biz, String appdiv, String userid) {
        // 读取元数据信息
        List<RealtimeMetricDefineEntity> metaList = dapanMapper.listRtKanban(biz, appdiv);

        // 读取当前的实时数据
        for (RealtimeMetricDefineEntity meta : metaList) {
            SeriesResult series = listSeries(dt, dt2, meta.indexTag, meta.indexTbl, meta.indexDim);
            meta.count = buildDapanCountEntity(series);
        }
        return metaList;
    }

    @Override
    public DimTableDataEntity listDashboardDimData(String bizTime, String appDiv, String biz, String dimId) {
        DapanDimConfig condition = new DapanDimConfig();
        condition.setAppDiv(appDiv);
        condition.setBiz(biz);
        condition.setDimId(dimId);
        DapanDimConfig dapanDimConfig = dapanDimConfigMapper.getDapanDimConfig(condition);
        String dataDimId = dapanDimConfig.getDimId();
        String indexEn = dapanDimConfig.getIndexEn();
        String indexCn = dapanDimConfig.getIndexCn();
        String dimNameCn = dapanDimConfig.getDimNameCn();
        String dimName = dapanDimConfig.getDimName();
//        String spellFields = spellFields(dimNameCn, indexEn, indexCn);

        String spellFields = spellFields(dimName, indexEn, indexCn);
        String outerFields = spellOuterFields(dimName, dimNameCn, indexEn, indexCn);
        String outerJoinSql = outerJoinSql(dimName, indexEn);

        HashMap<String, String> conditionMap = new HashMap<>();
        conditionMap.put("biz", biz);
        conditionMap.put("dimId", dimId);
        conditionMap.put("spellFields", spellFields);
        conditionMap.put("outerFields", outerFields);
        conditionMap.put("outerJoinSql", outerJoinSql);
        String[] indexEnSplit = indexEn.split(",");
        if (indexEnSplit.length > 0) {
            conditionMap.put("orderCol", indexEnSplit[0]);
        }
        conditionMap.put("startDt", bizTime);
        conditionMap.put("endDt", bizTime);

        List<Map<String, Object>> res = dimensionFlowBiMapper.selectByCondition(conditionMap);
        if (res != null && res.size() > 0) {
            DimTableDataEntity dimTableDataEntity = new DimTableDataEntity();
            dimTableDataEntity.setDimId(dataDimId);
            dimTableDataEntity.setDimNameCn(dimNameCn);
            dimTableDataEntity.setIndexCn(indexCn);
            dimTableDataEntity.setIndexEn(indexEn);
            dimTableDataEntity.setConfigId(dapanDimConfig.getId() + "");
            dimTableDataEntity.setBiz(dapanDimConfig.getBiz());
            dimTableDataEntity.setAppTitle(dapanDimConfig.getAppTitle());
            dimTableDataEntity.setBizTime(bizTime);
            dimTableDataEntity.setAppDiv(appDiv);
            dimTableDataEntity.setTableData(res);
            ArrayList<String> colList = new ArrayList<>();
            colList.add("业务日期");
            List<String> dimColList = Arrays.stream(dimNameCn.split("\\^")).collect(Collectors.toList());
            List<String> metricColList = Arrays.stream(indexCn.split(",")).collect(Collectors.toList());
            colList.addAll(dimColList);
            colList.addAll(metricColList);
            dimTableDataEntity.setColumnNames(colList);
            return dimTableDataEntity;
        }
        return null;
    }

    private String outerJoinSql(String dimName, String indexEn) {
        String[] dimNameSplit = dimName.split("\\^");
        String lastDate = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < dimNameSplit.length; i++) {
            int curTabIndex = i + 1;
            String colName = dimNameSplit[i];
            sb.append("\nleft join (select dim,dim_value,dim_value_cn from dim_zp_n_dapan_dict where v_date='").append(lastDate)
                    .append("' and dim ='").append(colName).append("') d").append(curTabIndex)
                    .append(" on d").append(curTabIndex).append(".`dim_value` = r.`").append(colName).append("`");
        }
        return sb.toString();
    }

    private String spellOuterFields(String dimName, String dimNameCn, String indexEn, String indexCn) {
        StringBuilder sb = new StringBuilder();
        sb.append("r.dt as '业务日期',");
        String[] dimNameCnSplit = dimNameCn.split("\\^");
        String[] dimNameSplit = dimName.split("\\^");
        for (int i = 0; i < dimNameSplit.length; i++) {
            String colCnName = dimNameCnSplit[i];
            String colName = dimNameSplit[i];
            sb.append("r.`").append(colName).append("`,");
            sb.append("ifnull(d").append(i + 1).append(".`dim_value_cn`, r.`").append(colName).append("`)");
            sb.append(" as '").append(colCnName).append("',");
        }
        String[] metricEnSp = indexEn.split(",");
        String[] metricCnSp = indexCn.split(",");
        for (int i = 0; i < metricEnSp.length; i++) {
            String metricEn = metricEnSp[i];
            String metricCn = metricCnSp[i];
            sb.append("r.`").append(metricEn).append("` as '").append(metricCn).append("'");
            if (i != metricCnSp.length - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    @Override
    public DimIndexChartEntity getDimIndexChart(String dimId, String dimValue, String indexEn, String indexCn, String dimValueCn, String biz) {
        String startDt = LocalDate.now().minusDays(31).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endDt = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        HashMap<String, String> conditionMap = new HashMap<>();
        conditionMap.put("dimId", dimId);
        conditionMap.put("dimValue", dimValue);
        conditionMap.put("startDt", startDt);
        conditionMap.put("endDt", endDt);
        if (StringUtils.isEmpty(biz)) {
            biz = "58";
        }
        conditionMap.put("biz", biz);

        String spellField = "get_json_string(value,'$." + indexEn + "') as metric_value";
        conditionMap.put("spellField", spellField);

        List<Map<String, Object>> res = dimensionFlowBiMapper.selectDimIndexByCondition(conditionMap);

        List<List<Object>> rows = new ArrayList<>();
        for (Map<String, Object> map : res) {
            List<Object> row = new ArrayList<>();
            String dt = map.get("dt").toString();
            Object metric_value = map.get("metric_value");
            row.add(dt);
            row.add(metric_value);
            rows.add(row);
        }
        DimIndexChartEntity chartEntity = new DimIndexChartEntity();
        chartEntity.setDimId(dimId);
        chartEntity.setDimValue(dimValue);
        chartEntity.setDimValueCn(dimValueCn);
        chartEntity.setIndexEn(indexEn);
        chartEntity.setIndexCn(indexCn);
        chartEntity.setChartData(rows);
        return chartEntity;
    }

    @Override
    public List<QuotaEntity> listWebMetricAll(String startTime, String endTime, String biz, String appDiv, String platform) {

        if (StringUtil.isEmpty(platform)) {
            platform = "ALL";
        }
//        List<QuotaEntity> metrics = dapanMapper.listDapanByDiv(startTime, endTime, biz, appDiv, null, null, platform);
        // todo: 取数表感觉有问题
        List<QuotaEntity>  metrics = metricDefineMapper.listMetric4DapanByDiv(startTime, endTime, biz, appDiv, null, null, platform);
        List<QuotaEntity>  dataList = dorisDapanMapper.listDapanByDiv(startTime, endTime, biz, appDiv, null, null, platform);
        Map<String, QuotaEntity> mapData = convertListToMap(dataList);
        for(QuotaEntity item: metrics) {
            QuotaEntity data = mapData.get(item.metaID);
            if(data==null) {
                continue;
            }
            item.content = data.content;
            item.contentInc1 = data.contentInc1;
            item.contentInc7 = data.contentInc7;
            item.reportDate = data.reportDate;
            item.createTime = data.createTime;
        }

        List<QuotaEntity> nMetrics = metrics.stream().peek(x -> {
            String newContent = formatData(x.getContent(), x.getDataUnitType());
            x.setContent(newContent);
        }).collect(Collectors.toList());

        List<QuotaEntity> coreMetrics = nMetrics.stream().filter(x -> x.getZlevel() == 1).collect(Collectors.toList());

        Map<String, List<QuotaEntity>> groupMetrics = nMetrics.stream().map(x -> {
            QuotaEntity entity = new QuotaEntity();
            BeanUtils.copyProperties(x, entity);
            return entity;
        }).collect(Collectors.groupingBy(QuotaEntity::getZgroup));

        ArrayList<QuotaEntity> quotaEntities = new ArrayList<>();
        int idGen = 1;
        for (QuotaEntity coreMetric : coreMetrics) {
            String zgroup = coreMetric.getZgroup();
            int pvid = idGen;
            coreMetric.setVid(pvid);
            coreMetric.setPVid(0);

            quotaEntities.add(coreMetric);
            List<QuotaEntity> subMetrics = groupMetrics.get(zgroup);
            for (QuotaEntity subMetric : subMetrics) {
                subMetric.setPVid(pvid);
                idGen = idGen + 1;
                subMetric.setVid(idGen);

                quotaEntities.add(subMetric);
            }
            idGen++;

        }
        return quotaEntities;
    }

    @Override
    public List<DapanDimConfig> listDapanDimConfig(String bizTime, String appDiv, String biz) {
        DapanDimConfig condition = new DapanDimConfig();
        condition.setAppDiv(appDiv);
//        condition.setBiz(biz);
        return dapanDimConfigMapper.listDapanDimConfig(condition);
    }

    private String spellFields(String dimName, String indexEn, String indexCn) {
        StringBuilder sb = new StringBuilder();
        String[] split = dimName.split("\\^");
        for (int i = 0; i < split.length; i++) {
            String colName = split[i];
            sb.append("split(dim_value,'^')[").append(i + 1).append("]");
            sb.append(" as '").append(colName).append("',");
        }
        String[] metricEnSp = indexEn.split(",");
        String[] metricCnSp = indexCn.split(",");
        for (int i = 0; i < metricEnSp.length; i++) {
            String metricEn = metricEnSp[i];
            String metricCn = metricCnSp[i];
            sb.append("cast(get_json_string(value,'$.");
            sb.append(metricEn).append("') as decimal(18,9))").append(" as '").append(metricEn).append("'");
            if (i != metricCnSp.length - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    private DapanCountEntity buildDapanCountEntity(SeriesResult series) {
        if (series == null || series.points == null) {
            return null;
        }
        List<List<Object>> points = series.points;
        int len = points.size();
        if (len < 1) {
            return null;
        }
        // 读取当前点的预估值
        double exp = Math.random() * 0.2;
        List<Object> point = points.get(len - 1);
        if (len < 2) {
            return DapanCountEntity.build2(0.0, exp, point.get(1));
        }
        // 计算t+1的涨幅
        List<Object> point1 = points.get(len - 2);
        double inc1 = 0.0;
        try {
            double d0 = Double.valueOf(point.get(1).toString());
            double d1 = Double.valueOf(point1.get(1).toString());
            inc1 = (d0 - d1) / d1;
            log.info("{}:{},{}:{}", point.get(0), point.get(1), point1.get(0), point1.get(1));
        } catch (Exception e) {
            log.info("exception: {}.", e.toString());
        }
        return DapanCountEntity.build2(inc1, exp, point.get(1));
    }


    private final Map<String, DeductionPolicy> dpMap = new HashMap<>();
    @PostConstruct
    private void initialize() {
        // 金额不一致
        dpMap.put("BE001", DeductionPolicy.build("5,20,40,50", "3,5,8,10", 9));
        dpMap.put("BE002001", DeductionPolicy.build("1,5,10,20", "3,5,8,10", 8));
        dpMap.put("BE002002", DeductionPolicy.build("10,20,30,40", "3,5,8,10", 5));
        dpMap.put("BE002003", DeductionPolicy.build("1000,4000,6000,8000", "3,5,8,10", 2));
        dpMap.put("BE002004", DeductionPolicy.build("1,5,10,20", "3,5,8,10", 8));
        dpMap.put("BE002005", DeductionPolicy.build("1,5,10,20", "3,5,8,10", 5));
        dpMap.put("BE002006", DeductionPolicy.build("50,100,150,200", "3,5,8,10", 5));

        int powerSum = 0;
        for (DeductionPolicy dp : dpMap.values()) {
            powerSum += dp.power;
        }
        //for (Map.Entry<String, DeductionPolicy> entry : dpMap.entrySet()) {
        for (DeductionPolicy dp : dpMap.values()) {
            //DeductionPolicy dp = entry.getValue();
            dp.ratio = dp.power * 10000 / powerSum;
            //log.info("{}, {}", entry.getKey(), dp.ratio);
        }

    }

    private Double calculateScore(String id, BigDecimal count) {
        if (count == null || (count.longValue() == 0)) {
            log.info("calculateScore count is invalid. {}", count);
            return null;
        }
        DeductionPolicy dp = dpMap.get(id);
        if (dp == null) {
            log.warn("calculateScore rule is undefined. {}", id);
            return null;
        }
        // 二分查找
        int idx = binarySearch(dp.point, count.longValue());
        if (idx == -1) {
            return null;
        }
        return dp.score[idx] * dp.ratio / 1000;
    }

    static class DeductionPolicy {
        public int power; // 权重
        public float ratio; // 比重
        public long[] point;
        public double[] score;

        public static DeductionPolicy build(String point, String score, int power) {
            String[] points = point.split(",");
            String[] scores = score.split(",");
            int points_len = points.length;
            int scores_len = scores.length;
            if (points_len != scores_len) {
                log.error("rule is invalid.{}, {}", point, score);
                return null;
            }
            DeductionPolicy thiz = new DeductionPolicy();
            thiz.power = power;
            thiz.point = new long[points_len];
            thiz.score = new double[scores_len];
            for (int i = 0; i < points_len; ++i) {
                thiz.point[i] = Long.valueOf(points[i]);
                thiz.score[i] = Double.valueOf(scores[i]);
            }
            return thiz;
        }
    }

    /**
     * 查找最后一个等于或者小于key的元素
     *
     * @param arr 数组
     * @param k   查找K
     * @return 数组位置
     */
    private static int binarySearch(long[] arr, long k) {
        int right = arr.length - 1;
        int left = 0;
        while (left <= right) {
            int mid = (right + left) / 2;
            if (arr[mid] == k) {
                return mid;
            } else if (arr[mid] < k) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return right;
    }
}
